package com.zp.demo.repush.aop;

import com.zp.demo.repush.RePushUtils;
import com.zp.demo.repush.anno.RePushCutAnno;
import com.zp.demo.repush.enums.RePushType;
import com.zp.demo.repush.event.RepayPlanEvent;
import com.zp.demo.repush.listener.RePush4LocalListener;
import com.zp.demo.repush.service.DoRePushExcuteService;
import com.zp.demo.repush.service.defined.DefindRePushDbService;
import com.zp.demo.repush.service.func.IsSucessFunc;
import com.zp.demo.repush.service.defined.DefinedRePushFitter;
import com.zp.demo.repush.service.func.ReSubmitResultFunc;
import com.zp.demo.repush.service.func.StrategyKeyFunc;
import com.zp.demo.repush.vo.BaseRePushVo;
import com.zp.demo.repush.vo.RePushProperties;
import com.zp.demo.repush.vo.RePushStrategyEntity;
import com.zp.demo.service.impl.IsSucessTrue;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;

/**
 * 需要重推的全部拦截 可以满足service，controler所有方法层。之前的只能是 controller层
 */
@Component
@Aspect
@Log4j2
public class RePushServiceAop {
    @Autowired
    private DefinedRePushFitter rePushFitter;
    @Autowired
    private RePushProperties rePushProperties;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ConcurrentMap<String,String> lockMap;
    @Pointcut("@annotation(pushAnno)")
    public void rePushCutAnno(RePushCutAnno pushAnno) {
    }
    @Around("rePushCutAnno(pushAnno)")
    public Object around(ProceedingJoinPoint pjp, RePushCutAnno pushAnno) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 先判断是否已经成功
        Object result;
        Class<?> clazz = methodSignature.getDeclaringType();
        Method targetMethod = methodSignature.getMethod();
        String key = this.getKey(methodSignature,pjp.getArgs());
        // 结果唯一性验证
        if(pushAnno.onlyOnce()){
            boolean isFillter = rePushFitter.checkSuccessOnes(key, rePushProperties.getSucessOnceSleepTime());
            if(!isFillter){
                // 重复提交，正在被提交中
                Assert.notNull(pushAnno.reSubmitResult(), "未设置 重复提交的异常返回结果：ReSubmitResultFunc 未传入值");
                Class<?> reSubmitResult = pushAnno.reSubmitResult();
                ReSubmitResultFunc reSubmitResultFunc = (ReSubmitResultFunc)reSubmitResult.newInstance();
                return reSubmitResultFunc.reSubmitResult();
            }
        }
        try {

            result = pjp.proceed();
            Class<?> isOkFun = pushAnno.isOkClazz();
            IsSucessFunc isSucessFunc = (IsSucessFunc) isOkFun.newInstance();
            boolean isTrue = isSucessFunc.isSucess(result);
            // 结果是正确的
            if(isTrue){
                return result;
            }else {
                if (pushAnno.onlyOnce()) {
                    rePushFitter.removeCheckSuccess(key);
                }
                // 如果存在 防死循环
                if (lockMap.putIfAbsent(key, "1") != null) {
                    return result;
                }
                // 获取重发 策略
                Class<?> strategyKeyClass = pushAnno.strategyKey();
                StrategyKeyFunc strategyKeyFunc = (StrategyKeyFunc) strategyKeyClass.newInstance();
                String strategyKey = strategyKeyFunc.getStrategyKey(result);
                Object repushResult = dopush(clazz, targetMethod, strategyKey, strategyKey, pjp.getArgs(), pushAnno.onlyOnce(), key);
                if (repushResult != null) {
                    return repushResult;
                }
                return result;
            }
        }catch (Exception e){
            if(pushAnno.onlyOnce()){
                rePushFitter.removeCheckSuccess(key);
            }
            // 如果存在
            if (lockMap.putIfAbsent(key, "1") != null) {
                throw e;
            }
            Object repushResult = dopush(clazz,targetMethod,e.getClass().getName(),e.getMessage(),pjp.getArgs(),pushAnno.onlyOnce(),key);
            if(repushResult != null){
                return repushResult;
            }
            throw e;
        }
    }
    private Object dopush(Class<?> clazz,Method method,String key,String mess,Object[] args,boolean onlyOnce,String onlyOnceKey){
        // 创建lock
           try {
               lockMap.putIfAbsent(onlyOnceKey, "1");
               RepayPlanEvent event = new RepayPlanEvent(applicationContext);
               BaseRePushVo pushVo = RePushUtils.getBaseRePush4Aop(clazz,method,key,mess,args);
               pushVo.setOnlyOnce(onlyOnce);
               pushVo.setOnlyOnceKey(onlyOnceKey);
               event.setRePushVo(pushVo);
               applicationContext.publishEvent(event);
               if(event.getRePushVo().getOldResult() != null){
                   return event.getRePushVo().getOldResult();
               }
           }catch (Exception e){
            log.error("出错了");
            throw e;
           }finally {
                lockMap.remove(onlyOnceKey);
           }
            return null;
    }
    public String getKey( MethodSignature methodSignature,Object[] args ){
        Class<?> clazz = methodSignature.getDeclaringType();
        Method targetMethod = methodSignature.getMethod();
        String methodId = RePushUtils.getMethodId(clazz, targetMethod);
        // 获取app有待改进
        String key = RePushUtils.generKey(methodId,args);
        return key;
    }

}
